﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using NetTopologySuite.Geometries;
using NetTopologySuite.Algorithm.Hull;
using NetTopologySuite.Index.Strtree;
using PointAsLine;

namespace PointAsLine
{
    public static class GroupProcessor
    {
        public static List<List<PointF2G>> GetGroups(PointF[] pointsF, double threshold = 2)
        {
            if (pointsF == null || pointsF.Length == 0)
                return null;

            // 转换为NTS的Coordinate数组
            var coordinates = pointsF.Select(p => new Coordinate(p.X, p.Y)).ToArray();
            var geometryFactory = new GeometryFactory();

            // 1. 分组：找出所有靠近的点组成的群组
            List<Geometry> groups = FindGroups(coordinates, threshold, geometryFactory);

            List<List<PointF2G>> res = new List<List<PointF2G>>();
            for (var i = 0; i < groups.Count; i++)
            {
                List<PointF2G> ts = new List<PointF2G>();
                for (var j = 0; j < groups[i].Coordinates.Length; j++)
                {
                    ts.Add(new PointF2G((float)(groups[i].Coordinates[j].X), (float)(groups[i].Coordinates[j].Y), i));
                }
                res.Add(ts);
            }
            return res;
        }
        /// <summary>
        /// 处理点集，移除小群组
        /// </summary>
        /// <param name="pointsF">输入的点集</param>
        /// <param name="threshold">判断是否为同一群组的距离阈值</param>
        /// <param name="wdThreshold">群组长宽的最小阈值，小于此值的群组将被移除</param>
        public static void Remove(ref PointF[] pointsF, double threshold = 2, double wdThreshold = 10)
        {
            if (pointsF == null || pointsF.Length == 0)
                return;

            // 转换为NTS的Coordinate数组
            var coordinates = pointsF.Select(p => new Coordinate(p.X, p.Y)).ToArray();
            var geometryFactory = new GeometryFactory();

            // 1. 分组：找出所有靠近的点组成的群组
            List<Geometry> groups = FindGroups(coordinates, threshold, geometryFactory);

            // 要保留的点列表
            List<Coordinate> pointsToKeep = new List<Coordinate>();

            // 处理每个群组
            foreach (var group in groups)
            {
                // 2. 使用NetTopologySuite计算凹包
                Geometry concaveHull = CalculateConcaveHull(group, threshold);

                // 3. 计算群组的长度和宽度
                var envelope = concaveHull.EnvelopeInternal;
                double width = envelope.Width;
                double height = envelope.Height;

                // 4. 判断是否保留群组
                if (width >= wdThreshold || height >= wdThreshold)
                {
                    // 将群组中的所有点加入保留列表
                    foreach (Coordinate coord in group.Coordinates)
                    {
                        pointsToKeep.Add(coord);
                    }
                }
                // 否则不加入pointsToKeep，相当于删除
            }

            // 转换回PointF数组
            pointsF = pointsToKeep.Select(c => new PointF((float)c.X, (float)c.Y)).ToArray();
        }

        // 1. 找出所有靠近的点组成的群组 (返回Geometry集合)
        private static List<Geometry> FindGroups(Coordinate[] coordinates, double threshold, GeometryFactory factory)
        {
            List<Geometry> groups = new List<Geometry>();
            bool[] visited = new bool[coordinates.Length];

            // 创建STRtree空间索引加速邻近查询
            var tree = new STRtree<Coordinate>();
            for (int i = 0; i < coordinates.Length; i++)
            {
                tree.Insert(new Envelope(coordinates[i]), coordinates[i]);
            }

            for (int i = 0; i < coordinates.Length; i++)
            {
                if (!visited[i])
                {
                    List<NetTopologySuite.Geometries.Point> groupCoords = new List<NetTopologySuite.Geometries.Point>();
                    Queue<int> queue = new Queue<int>();
                    queue.Enqueue(i);
                    visited[i] = true;

                    while (queue.Count > 0)
                    {
                        int current = queue.Dequeue();
                        groupCoords.Add(new NetTopologySuite.Geometries.Point(coordinates[current].X, coordinates[current].Y));

                        // 使用空间索引查询邻近点
                        var env = new Envelope(
                            coordinates[current].X - threshold,
                            coordinates[current].X + threshold,
                            coordinates[current].Y - threshold,
                            coordinates[current].Y + threshold);

                        var nearby = tree.Query(env)
                            .Where(c => !visited[Array.IndexOf(coordinates, c)]
                                    && coordinates[current].Distance(c) <= threshold)
                            .ToList();

                        foreach (var neighbor in nearby)
                        {
                            int index = Array.IndexOf(coordinates, neighbor);
                            visited[index] = true;
                            queue.Enqueue(index);
                        }
                    }

                    if (groupCoords.Count > 1) // 至少两个点才能形成群组
                    {
                        groups.Add(factory.CreateMultiPoint(groupCoords.ToArray()));
                    }
                }
            }

            return groups;
        }

        // 2. 使用NetTopologySuite计算凹包
        private static Geometry CalculateConcaveHull(Geometry group, double threshold)
        {
            // 使用ConcaveHull算法
            // 这里使用threshold作为最大边长参数
            var concaveHull = new ConcaveHull(group)
            {
                // 设置HolesAllowed为false可以防止生成带孔的多边形
                HolesAllowed = false
            }.GetHull();

            // 如果结果为空或不是多边形，则返回凸包作为后备
            if (concaveHull == null || concaveHull.IsEmpty || !(concaveHull is NetTopologySuite.Geometries.Polygon))
            {
                return group.ConvexHull();
            }

            return concaveHull;
        }
    }
}
